﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using LumenWorks.Framework.IO.Csv;
using System.IO;
using System.DirectoryServices;
using System.ComponentModel;
using System.DirectoryServices.AccountManagement;

namespace ImageUploader.domain
{
    class LDAPUser
    {

        public string[] Attributes
        {
            get;
            private set;
        }

        public string[] Values
        {
            get;
            private set;
        }


        public LDAPUser(string[] attributes,string[] values)
        {            
            if (attributes.Length != values.Length)
                throw new ArgumentException("Length of attributes & values arrays must be equal.");
            Attributes = attributes;
            Values = values;
        }
    }

    public class ImportUsers
    {
        [TypeConverter(typeof(EnumToStringUsingDescription))]
        public enum ImageFormat
        {
            [Description("No Images")]
            None=0,
            [Description("Image file path")]
            Imagefilepath=1,
            [Description("Byte array")]
            Bytearray=2
        }

        private string csvFilePath;
        //private LDAPConnection ldapConn;
        private DirectoryEntry usersEntry;
        private bool saveOverwrite = false;
        private bool savedOverwrite = false;
        private List<LDAPUser> ldapUsers = new List<LDAPUser>();
        private int cnIndex=-1;
        private string[] headers;
        private int usersProcessed=0;
        private int jpgPhotoIndex = -1;
        private ImageFormat imgFormat;
      //  private PrincipalContext ctx;
        private List<DirectoryEntry> nonCriticalUsers;
        private DomainController domainController;
        public ImportUsers(string csvFilePath,/* LDAPConnection ldapConn,*/ DirectoryEntry usersEntry,ImageFormat imgFormat, DomainController dc)
        {
            this.domainController=dc;
            this.csvFilePath = csvFilePath;
            //this.ldapConn = ldapConn;
            this.usersEntry = usersEntry;
            this.imgFormat = imgFormat;
     //        = new PrincipalContext(ContextType.Domain,"","","","",""
     //       ctx.
            this.nonCriticalUsers = new List<DirectoryEntry>(dc.GetNonSystemCriticalUsers(null).ConvertAll(u=>u.DirectoryEntry));
        }

        public void Import()
        {            
            try
            {
                using (CsvReader csv = new CsvReader(new StreamReader(csvFilePath), true, ';'))
                {
                    int fieldCount = csv.FieldCount;
                    headers = csv.GetFieldHeaders();
                    //jpgPhotoIndex = imgFormat == 0 ? -1 : Array.IndexOf(headers, "jpegPhoto");
                    jpgPhotoIndex =Array.IndexOf(headers, "jpegphoto");
                    cnIndex = Array.IndexOf(headers, "cn");
                    if (cnIndex == -1)
                        throw new ArgumentException("Every record of the CSV-file should contain a \"cn\" field");

                    while (csv.ReadNextRecord())
                    {
                        string[] values = new string[fieldCount];
                        csv.CopyCurrentRecordTo(values);
                        string username = values[cnIndex];
                        ldapUsers.Add(new LDAPUser(headers, values));
                    }
                }
            }
            catch (Exception e)
            {
                throw new Exception("Import failed to import users\nReason: Reading CSV failed\n" + e.Message);
            }
            try
            {
                AddUsers();
            }
            catch (Exception exe)
            {
                if (!(exe is AbortedException))
                    throw new Exception("Import failed, reason:\n" + exe.Message);
                else throw exe;
            }
        }

        

        public event ImageUploader.domain.Delegates.ProgressHandler ProgressChanged;

        

        public void AddUsers()
        {
            foreach (LDAPUser user in ldapUsers)
            {
                try
                {
                    AddUser(user);
                }
                catch (DirectoryServicesCOMException e)
                {
                    if (e.ExtendedError == 8305 /*|| e.ExtendedError==1316*/)
                    {
                        if (!saveOverwrite)
                        {
                            OverwriteAnswer owa = GUIController.ShowOverwriteUserDialog(user.Values[cnIndex]);
                            switch (owa.Answer)
                            {
                                case ImageUploader.domain.OverwriteAnswer.OverWriteResult.Cancel:
                                    throw new AbortedException();

                                case ImageUploader.domain.OverwriteAnswer.OverWriteResult.Overwrite:
                                    UpdateUser(user);
                                    savedOverwrite = true;
                                    break;

                                case ImageUploader.domain.OverwriteAnswer.OverWriteResult.Skip:
                                    savedOverwrite = false;
                                    break;
                            }
                            saveOverwrite = owa.Save;
                        }
                        else if (savedOverwrite) //not skipped
                            UpdateUser(user);
                    }
                    else throw e;
                }
                usersProcessed++;                
                if (ProgressChanged != null)
                    ProgressChanged(this, new ProgressEventArgs((int)((((float)usersProcessed / ldapUsers.Count))*100)));
            }
        }



        private void UpdateUser(LDAPUser user)
        {          


            DirectorySearcher ds = new DirectorySearcher(usersEntry);
            ds.Filter = "(&(objectClass=user) (cn=" + user.Values[cnIndex] + "))";
            headers.ToList().ForEach(h => ds.PropertiesToLoad.Add(h));
            SearchResult foundUser=ds.FindOne();
            try
            {
                using (DirectoryEntry deFound = foundUser.GetDirectoryEntry())
                {
                    if (StaticMethods.DirectoryEntryListContainsDE(nonCriticalUsers,deFound)/*nonCriticalUsers.Contains(deFound)*/)
                    {
                        string cn = deFound.Properties["cn"].Value.ToString();
                        deFound.Parent.Children.Remove(deFound);
                        deFound.CommitChanges();
                        DirectoryEntry newDe =deFound.Parent.Children.Add("CN="+cn, "user");
                        newDe.CommitChanges();
                        ProcessUser(newDe, user);
                    }
                }
            }
            catch (Exception)
            {
                // foundUser.GetDirectoryEntry().DeleteTree();
                try
                {
                    //foundUser.GetDirectoryEntry().DeleteTree();

                    usersEntry.Children.Remove(foundUser.GetDirectoryEntry());
                    AddUser(user);
                }
                catch (Exception ex)
                {
                    if (foundUser != null)
                    {
                        using (DirectoryEntry deUser = foundUser.GetDirectoryEntry())
                        {
                            foreach (PropertyValueCollection p in deUser.Properties)
                                try
                                {
                                    p.Clear();
                                    deUser.CommitChanges();
                                }
                                catch (Exception)
                                {
                                    deUser.RefreshCache();
                                }
                            ProcessUser(deUser, user);
                        }
                    }
                    //}
                }
            }
        }
        private void ProcessUser(DirectoryEntry deUser,LDAPUser ldapUser)        
        {            
            for (int i = 0; i < headers.Length; i++)
                if (ldapUser.Values[i].Length > 0 && i!=cnIndex)  //i != cnIndex TODO : !!!!!!!!!!
                {
                    if (i == jpgPhotoIndex)
                    {
                        if (imgFormat == ImageFormat.Imagefilepath)
                            try
                            {
                                deUser.Properties[headers[i]].Value = ImageOperations.GetByteArrayFromImagePath(ldapUser.Values[i]);
                            }
                            catch (Exception)
                            { }
                        else if (imgFormat == ImageFormat.Bytearray)
                        {
                          //  System.Drawing.Image img = ImageOperations.GetImageFromByteArray((byte[])ldapUser.Values[i]);
                         //   foreach(byte b in ldapUser.Values[i].
                            //BitConverter. ldapUser.Values[i].ToString()
                            string bytesString=ldapUser.Values[i].ToString();
                            string[] sArray = bytesString.Substring(1,bytesString.Length-1).Split('\\');
                            byte[] bArray= new byte[sArray.Length];
                            for (int bCount = 0;bCount<bArray.Length;bCount++)
                                bArray[bCount] = byte.Parse(sArray[bCount].ToUpper(), System.Globalization.NumberStyles.HexNumber);
                            deUser.Properties[headers[i]].Value = bArray;
                           // deUser.Properties[headers[i]].Value= 
                           // deUser.Properties[headers[i]].Value = ldapUser.Values[i];
                        }
                        deUser.CommitChanges();
                    }
                    else
                    {
                        try
                        {
                            deUser.Properties[headers[i]].Value = ldapUser.Values[i];
                            deUser.CommitChanges();
                        }
                        catch (DirectoryServicesCOMException e)
                        {
                          //  if (e.ExtendedError == 8346 || e.ExtendedError==8369 || e.ExtendedError==8311 || e.ExtendedError==1316 ||)
                          //      deUser.RefreshCache();
                          //  else throw e;
                        }
                    }
                    
                }
            
        }

        private void AddUser(LDAPUser user)
        {
         /*   DirectorySearcher ds = new DirectorySearcher(usersEntry);
            ds.Filter = "(&(objectClass=user) (cn=" + user.Values[cnIndex] + "))";
            headers.ToList().ForEach(h => ds.PropertiesToLoad.Add(h));
            SearchResult foundUser = ds.FindOne();
            //  foundUser.GetDirectoryEntry().DeleteTree();
             usersEntry.Children.Remove(foundUser.GetDirectoryEntry());
            */


            using (DirectoryEntry newUser = usersEntry.Children.Add("CN=" + user.Values[cnIndex], "user"))
            {
                newUser.CommitChanges();
                ProcessUser(newUser, user);
            }
            /*  for (int i = 0; i < headers.Length; i++)
              {
                  if (user.Values[i].Length > 0)
                      if (i == jpgPhotoIndex)
                      {
                          if (imgFormat == ImageFormat.Imagefilepath)
                              try
                              {
                                  newUser.Properties[headers[i]].Value = ImageOperations.GetByteArrayFromImagePath(user.Values[i]);
                              }
                              catch (Exception)
                              { }
                          else if (imgFormat == ImageFormat.Bytearray)
                              newUser.Properties[headers[i]].Value = user.Values[i];
                      }
                      else newUser.Properties[headers[i]].Value = user.Values[i];
              }
              newUser.CommitChanges();
              newUser.Close();*/
        }
    }
}
